package com.gitee.yangtianle.redisson.seckill.interceptor;


import com.gitee.yangtianle.redisson.seckill.annotation.RedissonDispersedLimit;
import com.gitee.yangtianle.redisson.seckill.exception.RedissonLimitException;
import com.gitee.yangtianle.redisson.seckill.config.RedissonMapProperties;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.IntegerCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author tianle.ytl
 * @date 2018/9/19
 */
public class RedissonLimitInterceptor implements HandlerInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(RedissonLimitInterceptor.class);

    private RedissonClient redissonClient;
    private RedissonMapProperties redissonMapProperties;

    public RedissonLimitInterceptor(RedissonClient redissonClient, RedissonMapProperties redissonMapProperties) {
        this.redissonClient = redissonClient;
        this.redissonMapProperties = redissonMapProperties;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Class<?> clz = method.getDeclaringClass();
        RedissonDispersedLimit redissonDispersedLimit = method.getAnnotation(RedissonDispersedLimit.class);
        if (redissonDispersedLimit == null) return true;
        String topic = redissonMapProperties.getLimitMapTopic();
        if (topic == null) {
            logger.warn("Configure the properties in the properties file spring.redisson.limit-map-topic");
            throw new RedissonLimitException("The map of topic is null");
        }
        double limiter = redissonDispersedLimit.limiter();
        String key = redissonDispersedLimit.key();
        TimeUnit timeUnit = redissonDispersedLimit.unitTimeOut();
        long timeout = redissonDispersedLimit.appointedTime();

        RMapCache<String, Integer> cache = redissonClient.getMapCache(topic, IntegerCodec.INSTANCE);
        cache.putIfAbsent(key, 0, timeout, timeUnit);
        Integer count = cache.addAndGet(key, 1);
        boolean temp = count >= limiter;
        logger.info("{},{} The request {}. The number of = {}", clz.getName(), method.getName(), temp ? "success" : "failed", count);
        if (temp) {
            return true;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

    }

    public RedissonClient getRedissonClient() {
        return redissonClient;
    }

    public void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public RedissonMapProperties getRedissonMapProperties() {
        return redissonMapProperties;
    }

    public void setRedissonMapProperties(RedissonMapProperties redissonMapProperties) {
        this.redissonMapProperties = redissonMapProperties;
    }
}
